// Server
#include "server.h"

BEGIN_GOODS_NAMESPACE

#ifndef _WIN32
#include <sys/times.h>
#include <sys/socket.h>
#include <unistd.h>
extern "C" {
#include <netdb.h>
}

long GetTickCount ()
{
	tms tm;
	return times(&tm);
}

void Sleep (int ms)
{
	usleep (ms * 1000);
}

#endif //_WIN32

#define CS(mutex) critical_section __cs(mutex)

// Class CAbstractServer 

CAbstractServer::~CAbstractServer()
{
//	console::use_log_file(log , False);
	Close ();
}

int CAbstractServer::Open (int nport)
{
	CS(cs);

	msg_buf err;
	//struct hostent* hp;  // entry in hosts table
	//hp = gethostbyname("localhost");
	sprintf(name, "%s:%d", "localhost", nport);	
	n_opened_gateways = 0;
	global_gateway = NULL;
	local_gateway = NULL;
	clients.prune();	
	
	opened = true;
	global_gateway = socket_t::create_global(name); 
	if (global_gateway) { 
		if (global_gateway->is_ok()) { 
			n_opened_gateways = 1;
			task::create(start_global_gatekeeper, this);
		} else { 
			global_gateway->get_error_text(err, sizeof err);
			console::message(msg_error, 
				"Failed to Open global accept socket: %s\n",err);
			delete global_gateway;
			global_gateway = NULL; 
		}
	}
	local_gateway = socket_t::create_local(name); 
	if (local_gateway) { 
		if (local_gateway->is_ok()) { 
			cs.enter();
			n_opened_gateways += 1;
			cs.leave();
			task::create(start_local_gatekeeper, this);
		} else { 
			local_gateway->get_error_text(err, sizeof err);
			console::message(msg_error, 
				"Failed to Open local accept socket: %s\n", err);
			delete local_gateway;
			local_gateway = NULL; 
		}
	}
	
	if (n_opened_gateways == 0) { 
		console::message(msg_error, "Failed to create gateways\n");
		Close();
		return False;
	}
	return True;
}

unsigned int CAbstractServer::GetClientsNumber ()
{
	CS(cs);
	return n_clients;
}

const char* CAbstractServer::GetServerName () const
{
	CS(cs);
	return name;
}

void CAbstractServer::RemoveClientAgent (CClientAgent *agent)
{
	CS (cs);

	agent->unlink();
	n_clients --;

	// I>
	delete agent;			// I>?T6tI>
							// agent ?J1H4_3L=
	term_event.signal();	// M(V* Close() I> Agent
	return;
}

void CAbstractServer::Close ()
{
	// 1. close accepts
	opened = false;
	if (local_gateway != NULL) { 
		local_gateway->cancel_accept();
	}
	if (global_gateway != NULL) {
		global_gateway->cancel_accept();
	}
	while (1) {
		CS(cs);
		TRACE_MSG((msg_important, 
			"Close server: number of opened gateways: %d\n",
			n_opened_gateways));
		if (n_opened_gateways != 0)
		{
			term_event.reset();
			term_event.wait();
		}
		else
			break;
	}
	
	// 2. close clients
	CClientAgent* client = (CClientAgent*)clients.next;
	while (client != &clients) { 
		CClientAgent* next = (CClientAgent*)client->next;
		client->OnCancel();
		client = next;
	}
	while (1) {  
		TRACE_MSG((msg_important, 
			"Close server: waiting until all clients will "
			"be disconnected\n"));
		
		CS(cs);
		if (n_clients > 0)
		{
			term_event.reset();
			term_event.wait();
		}
		else
			break;
	}
	
	//
	// At this moment all server threads are terminated and no new
	// one can be created since gateways are closed
	//
	TRACE_MSG(( msg_important, "Close managers...\n"));
	delete local_gateway;
	delete global_gateway;
	
	TRACE_MSG((msg_important, "Database shutdown completed...\n"));
}

void CAbstractServer::SetMaxClientsNumber (unsigned int num)
{
	CS (cs);
	n_max_clients = num;
}

unsigned int CAbstractServer::GetMaxClientsNumber ()
{
	CS (cs);
	return n_max_clients;
}

void task_proc CAbstractServer::start_local_gatekeeper (void* arg)
{
	CAbstractServer* server = (CAbstractServer*)arg; 
	server->accept(server->local_gateway);
}

void task_proc CAbstractServer::start_global_gatekeeper (void* arg)
{
	CAbstractServer* server = (CAbstractServer*)arg; 
	server->accept(server->global_gateway);
}

void CAbstractServer::accept (socket_t* gateway)
{
	msg_buf buf; 
	while (opened) { 
		socket_t* new_sock = gateway->accept();
		if (new_sock != NULL && new_sock->is_ok()) { 
			if (!opened)
			{
				delete new_sock;
				break;
			}

			char login_name[255];
			char *p_name_buffer = new_sock->get_peer_name ();
			if (NULL != p_name_buffer)
			{
				strncpy (login_name, p_name_buffer, 255);
				delete[] p_name_buffer;
			}
			else
				strncpy (login_name, "UnknowClient", 255);

			// M;L+`#,H41
			unsigned int time_out = GetTickCount () + 1000;
			for ( ; GetClientsNumber () >= GetMaxClientsNumber(); )
			{
				if (time_out > GetTickCount())
					break;
				Sleep (10);
			}

			if (GetClientsNumber () >= GetMaxClientsNumber())
			{// connect number full
				delete new_sock;
#ifdef _DEBUG
				console::message(msg_login|msg_error|msg_time,
					"The client number is too large, connect to '%s' failed!\n", login_name);
#endif
				continue;
			}

			// create connection
			CClientAgent* client = OnCreateClientAgent();
			if (NULL == client)
			{
				delete new_sock;
#ifdef _DEBUG
				console::message(msg_login|msg_error|msg_time,
					"Create client agent '%s' failed!\n", login_name);
#endif
				continue;
			}

			// attach socket to client agent
			client->m_pServer = this;
			client->m_pSock = new_sock;
			// insert to the client list
			CS (cs);
			{
				client->link_after(&clients); 
				n_clients ++;
				// create thread and start communication
				client->ThreadRun();
				//console::message(msg_login|msg_time, "Start client: %s\n", login_name);
			}
		} else {
			if (opened) {
				gateway->get_error_text(buf, sizeof buf);
				console::message(msg_error|msg_time, 
					"Failed to accept socket: %s\n", buf);
			}
		}
	}
	
	// singal for Close ()
	cs.enter();
	if (--n_opened_gateways == 0) { 
		term_event.signal();
	}
	cs.leave();
}

END_GOODS_NAMESPACE
